﻿#region Copyright and Licensing Notice
// Copyright (C) 2011 Steven Bosnick
// 
// This program is open source software; you can redistribute it 
// and/or modifiy it under the terms of the Eclipse Public License,
// version 1.0.
// 
// This program is distributed in the hope that it will be useful, 
// but WITHOUT ANY WARRANTY; without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A 
// PARTICULAR PURPOSE. See the Eclipse Public License for more
// details.
// 
// You should have received a copy of the Eclipse Public License 
// along with this program; if not, you can find a copy at
// "http://www.opensource.org/licenses/eclipse-1.0.php".
#endregion
using System;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using BitwiseOre.SharedMemory;

namespace TestHost
{
    public static class Program
    {
        private static void Main(string[] args)
        {
            AppDomain.CurrentDomain.UnhandledException += UnhandledExceptionHandler;

            AcceptDelayedAfterWaitForTimesout();

            Console.WriteLine("Press enter...");
            Console.ReadLine();
        }

        private static void AcceptDelayedAfterWaitForTimesout()
        {
            const int pendingConnections = 2;
            var name = string.Format("TestListenerG{0}", pendingConnections);
            var barrier = new Barrier(2);

            var context = new SharedMemoryContext();

            context.ListenerContext.MaxPendingConnections = pendingConnections;

            using (var listener = SharedMemoryListener.Create(context, name))
            using (var factory = SharedMemoryFactory.Create(context, name))
            {
                listener.WaitForTimeout = 1000;
                listener.AcceptTimeout = 1000;
                factory.ConnectTimeout = 100;

                var async = listener.BeginWaitFor(null, null);

                factory.BeginConnect(
                    ar =>
                        {
                            try
                            {
                                factory.EndConnect(ar);
                            }
                            catch (TimeoutException)
                            {
                            }
                            finally
                            {
                                barrier.SignalAndWait();
                            }
                        }, null);

                listener.EndWaitFor(async);

                barrier.SignalAndWait();

                GC.Collect();
                GC.Collect();

                try
                {
                    listener.Accept();
                }
                catch (TimeoutException ex)
                {
                    Console.WriteLine("Expected exception of type: {0}", ex.GetType());
                }

                GC.Collect();
                GC.Collect();

            }
        }

        private static void UnhandledExceptionHandler(object sender, UnhandledExceptionEventArgs args)
        {
            Console.WriteLine("=============Start UnhandledExceptionHandler=============");
            var exception = args.ExceptionObject as Exception;
            if (exception == null)
            {
                Console.WriteLine("Exception object is not of type Exception.");
            }
            else
            {
                exception.Visit(PrintExceptionNode);
                Console.WriteLine();
                exception.Visit(PrintStackTraceNode);
                Console.WriteLine();
                exception.Visit(PrintCallContextNode);
            }
            Console.WriteLine("=============End UnhandledExceptionHandler=============");
        }

        private static void Visit(this Exception exception, Action<Exception, int> visistor)
        {
            Visit(exception, visistor, 0);
        }

        private static void Visit(Exception exception, Action<Exception, int> visitor, int depth)
        {
            visitor(exception, depth);
            var ae = exception as AggregateException;
            if (ae == null) return;

            foreach (var ex in ae.InnerExceptions)
            {
                Visit(ex, visitor, depth + 1);
            }
        }

        private static void PrintCallContextNode(Exception exception, int depth)
        {
            string message;
            if (exception.Data.Contains("CallContextStackTrace"))
            {
                var stacktrace = (StackTrace) exception.Data["CallContextStackTrace"];
                message = GetStackTraceMessage(stacktrace, 6);
            }
            else
            {
                message = "No contained stack trace";
            }
            PrintNode(exception, depth, message);
        }

        private static void PrintExceptionNode(Exception exception, int depth)
        {
            var message = exception.Message.Substring(0, Math.Min(30, exception.Message.Length));
            PrintNode(exception, depth, message);
        }

        private static void PrintStackTraceNode(Exception exception, int depth)
        {
            var stacktrace = new StackTrace(exception);
            var message = GetStackTraceMessage(stacktrace, 3);

            PrintNode(exception, depth, message);
        }

        private static void PrintNode(Exception exception, int depth, string message)
        {
            var prefix = GetPrefix(depth);
            var typename = exception.GetType().Name;
            Console.WriteLine("{0}{1}: {2}", prefix, typename, message);
        }

        private static string GetStackTraceMessage(StackTrace stacktrace, int traceLevels)
        {
            string message;
            if (stacktrace.FrameCount == 0)
            {
                message = "No stack trace.";
            }
            else
            {
                var methods = from frame in stacktrace.GetFrames()
                              let method = frame.GetMethod()
                              let type = method.DeclaringType
                              select method.Name;
                message = string.Join("; ", methods.Take(traceLevels));
            }
            return message;
        }

        private static string GetPrefix(int depth)
        {
            return (from i in Enumerable.Range(0, depth)
                    select "  ")
                .Aggregate(new StringBuilder(), (sb, s) => sb.Append(s))
                .ToString();
        }
    }
}